<?php

namespace app\service\jt808;

class JT808Service
{

    public static $sequenceNumber = 0; //流水号初始值

    /**
     * description  转成10进制字符串数组
     * @param $string 16进制字符串
     * @return array   10进制数组
     */
    public function get10Bytes(string $string): array
    {
        $bytes = array();
        $len = strlen($string);
        for ($i = 0; $i < $len; $i++) {
            $bytes[] = ord($string[$i]);
        }
        return $bytes;
    }

    /**
     * description  10进制字符串数组转成16进制字符串数组
     * @param string $data 10进制字符串数组
     * @return array 16进制字符串数组
     */
    public function getTo16Bytes(string $data): array
    {
        $content = bin2hex($data);
        $res = explode('7e7e', $content);
        $array = [];
        //解决粘包
        if (count($res) > 1) {
            foreach ($res as $k => $v) {
                if ($k == reset($res)) {
                    $array[$k] = str_split($v . '7e', 2);
                } else if ($k == end($res)) {
                    $array[$k] = str_split('7e' . $v, 2);
                } else {
                    $array[$k] = str_split('7e' . $v . '7e', 2);
                }
            }
        } else {
            $array[] = str_split($res[0], 2);
        }
        return $array;
    }

    /**
     * description   接受到的16进制字符补0  例如：01=>0x01
     * @param array $data 16进制数组
     * @return array  补0之后的16进制数组
     */
    public function supplementZero(array $data): array
    {
        $len = count($data);
        $res = [];
        for ($j = 0; $j < $len; $j++) {
            if (strlen($data[$j]) == 1) {
                $res[$j] = "0x" . "0" . $data[$j];
            } else {
                $res[$j] = "0x" . $data[$j];
            }
        }
        return $res;
    }

    /**
     * description  把一个4位的数组转化位整形
     * @param array $data 接受数组
     * @return int  返回int
     */
    public function bytesToInt(array $data): int
    {
        $res = [];
        foreach ($data as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        $temp0 = $res[0] & 0xFF;
        $temp1 = $res[1] & 0xFF;
        $temp2 = $res[2] & 0xFF;
        $temp3 = $res[3] & 0xFF;
        return (($temp0 << 24) + ($temp1 << 16) + ($temp2 << 8) + $temp3);
    }

    /**
     * description  BCD码转字符串
     * @param array $data 数组
     * @return bool|string  返回字符串
     */
    public function bcdToString(array $data): bool|string
    {
        $len = count($data);
        $temp = "";
        for ($i = 0; $i < $len; $i++) {
            // 高四位
            $temp .= (($data[$i] & 0xf0) >> 4);
            // 低四位
            $temp .= ($data[$i] & 0x0f);
        }
        return (substr($temp, 0, 1) == 0) ? substr($temp, 1) : $temp;
    }

    /**
     * description  从接受到的16进制数组中获取设备号数组
     * @param array $data 接受到的16进制数组
     * @return string 设备号id
     */
    public function getSensorId(array $data): string
    {
        $sensorArray = array_slice($data, 3, 6);
        $sensorArrayZero = $this->supplementZero($sensorArray);
        $res = [];
        foreach ($sensorArrayZero as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        return $this->bcdToString($res);
    }

    /**
     * description   把一个二字节数组转化成整型
     * @param array $data 二字节数组
     * @return int   整型
     */
    public function twoBytesToInteger(array $data): int
    {
        $res = [];
        foreach ($data as $k => $v) {
            $res[$k] = intval(base_convert($v, 16, 10));
        }
        $temp0 = $res[0] & 0xFF;
        $temp1 = $res[1] & 0xFF;
        return (($temp0 << 8) + $temp1);
    }

    /**
     * description  接受内容中4字节数组转成int
     * @param array $data 16进制字节数组
     * @param int $a 开始位
     * @return int   int值
     */
    public function getNum(array $data, int $a = 0): int
    {
        $numArray = array_slice($data, $a, 4);
        return $this->bytesToInt($numArray);
    }

    /**
     * description  按位异或
     * @param array $data
     * @return int
     */
    public function getEveryXor(array $data): int
    {
        $len = count($data);
        $rew = 0;
        for ($i = 1; $i < $len; $i++) {
            $rew = $rew ^ $data[$i];
        }
        return $rew;
    }

    /**
     * [checkCode 生成验证码带开头7e]
     * @param array $data
     * @return string [array]
     */
    public function checkCode(array $data): string
    {
        $sum = 0;
        //去掉开头的7e
        unset($data[0]);
        //for ($i = 0; $i < count($arr); $i++) {
        foreach ($data as $k => $v) {
            $sum = $sum ^ hexdec($v);
        }
        return str_pad(dechex($sum), 2, "0", STR_PAD_LEFT);
    }

    /**
     * description   将字节数组转为字符串
     * @param array $data 字节数组
     * @return string   返回字符串
     */
    public function bytesArrayToString(array $data): string
    {
        $str = '';
        foreach ($data as $ch) {
            $str .= chr($ch);
        }
        return $str;
    }

    /**
     * [arrayToBytes 返回二进制内容]
     * @param array $data
     * @return false|string [type][二进制内容]
     */
    public function arrayToBytes(array $data): bool|string
    {
        $ret = implode($data);
        return hex2bin($ret);
    }

    /**
     * description 拼接字符串
     * @param $str
     * @param int $n
     * @param string $char
     * @return string
     */
    public function getTurnStr($str, int $n = 1, string $char = '0'): string
    {
        for ($i = 0; $i < $n; $i++) {
            $str = $char . $str;
        }
        return $str;
    }

    /**
     * description  转成二进制字符串
     * @param $data array 16进制数组
     * @return string  字符串
     */
    public function getTwoStr(array $data): string
    {
        //转成2进制
        $str = array();
        $req = array();
        foreach ($data as $key => $value) {
            $str[$key] = base_convert($data[$key], 16, 2);
            $leng = 8 - strlen($str[$key]);
            $req[] = $this->getTurnStr($str[$key], $leng, "0");
        }
        //拼接字符串
        return implode("", $req);
    }

    /**
     * description  获取设备号
     * @param $data array 16进制数组
     * @param $length int 补全长度
     * @return bool|string   返回字符串设备号
     */
    public function getEquipmentNumber(array $data, int $length = 12): bool|string
    {
        $equipmentArray = array_slice($data, 5, 6);
        $res = [];
        foreach ($equipmentArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $equipmentNumber = $this->bcdToString($res);
        return str_pad($equipmentNumber, $length, "0", STR_PAD_LEFT);
    }

    /**
     * description  获取16进制数组来计算出设备号
     * @param $data 16进制数组
     * @return array  返回设备号数组
     */
    public function getEquipmentNumberArray(array $data): array
    {
        return array_slice($data, 5, 6);
    }

    /**
     * description  获取报警信息
     * @param $data array 16进制数组
     * @param $index  string 数组索引13
     * @param bool $type
     * @return array|string
     */
    public function getAlarmMessage(array $data, string $index, bool $type = false)
    {
        $alarmArray = $this->getTwoStr(array_slice($data, $index, 4));
        if ($type) {
            return $this->getAlarmMessageArray($alarmArray);
        }
        return $alarmArray;
    }

    /**
     * 数据解析
     * @param $alarmArray
     * @return array
     */
    protected function getAlarmMessageArray($alarmArray){
        $alarm['0'] = substr($alarmArray, -1, 1) == 1 ? '紧急报警，触动报警开关后触发' : '';
        $alarm['1'] = substr($alarmArray, -2, 1) == 1 ? '超速报警' : '';
        $alarm['2'] = substr($alarmArray, -3, 1) == 1 ? '疲劳驾驶' : '';
        $alarm['3'] = substr($alarmArray, -4, 1) == 1 ? '危险预警' : '';
        $alarm['4'] = substr($alarmArray, -5, 1) == 1 ? 'GNSS 模块发生故障' : '';
        $alarm['5'] = substr($alarmArray, -6, 1) == 1 ? 'GNSS 天线未接或被剪断' : '';
        $alarm['6'] = substr($alarmArray, -7, 1) == 1 ? 'GNSS 天线短路' : '';
        $alarm['7'] = substr($alarmArray, -8, 1) == 1 ? '终端主电源欠压' : '';
        $alarm['8'] = substr($alarmArray, -9, 1) == 1 ? '终端主电源掉电' : '';
        $alarm['9'] = substr($alarmArray, -10, 1) == 1 ? '终端 LCD 或显示器故障' : '';
        $alarm['10'] = substr($alarmArray, -11, 1) == 1 ? 'TTS 模块故障' : '';
        $alarm['11'] = substr($alarmArray, -12, 1) == 1 ? '摄像头故障' : '';
        $alarm['12'] = substr($alarmArray, -13, 1) == 1 ? '道路运输证 IC 卡模块故障' : '';
        $alarm['13'] = substr($alarmArray, -14, 1) == 1 ? '超速预警' : '';
        $alarm['14'] = substr($alarmArray, -15, 1) == 1 ? '疲劳驾驶预警' : '';
//            $alarm['17'] = substr($alarmArray, -18, 3) == 1 ? '' : '';
        $alarm['18'] = substr($alarmArray, -19, 1) == 1 ? '当天累计驾驶超时' : '';
        $alarm['19'] = substr($alarmArray, -20, 1) == 1 ? '超时停车' : '';
        $alarm['20'] = substr($alarmArray, -21, 1) == 1 ? '进出区域' : '';
        $alarm['21'] = substr($alarmArray, -22, 1) == 1 ? '进出路线' : '';
        $alarm['22'] = substr($alarmArray, -23, 1) == 1 ? '路段行驶时间不足/过长' : '';
        $alarm['23'] = substr($alarmArray, -24, 1) == 1 ? '路线偏离报警' : '';
        $alarm['24'] = substr($alarmArray, -25, 1) == 1 ? '车辆 VSS 故障' : '';
        $alarm['25'] = substr($alarmArray, -26, 1) == 1 ? '车辆油量异常' : '';
        $alarm['26'] = substr($alarmArray, -27, 1) == 1 ? '车辆被盗(通过车辆防盗器)' : '';
        $alarm['27'] = substr($alarmArray, -28, 1) == 1 ? '车辆非法点火' : '';
        $alarm['28'] = substr($alarmArray, -29, 1) == 1 ? '车辆非法位移' : '';
        $alarm['29'] = substr($alarmArray, -30, 1) == 1 ? '碰撞预警' : '';
        $alarm['30'] = substr($alarmArray, -31, 1) == 1 ? '侧翻预警' : '';
        $alarm['31'] = substr($alarmArray, -32, 1) == 1 ? '非法开门报警（终端未设置区域时，不判断非法开门）' : '';
        if($alarm){
            foreach ($alarm as $k=>$v) {
                if(!$v){
                    unset($alarm[$k]);
                }
            }
        }
        return $alarm;
    }

    /**
     * description   获取状态信息
     * @param $data array 16进制数组
     * @param $index string  数组索引
     * @param $type bool  返回数据还是原本信息
     * @return array|string
     */
    public function getPositionStatus(array $data, string $index, bool $type = false)
    {
        $positionArray = $this->getTwoStr(array_slice($data, $index, 4));
        if ($type) {
            //0 0：ACC 关；1： ACC 开
            $is_acc = substr($positionArray, -1, 1) == 1 ? 'ACC开' : 'ACC关';
            //判断是否定位，0定位，1未定位
            $isPosition = substr($positionArray, -2, 1) == 0 ? $isPosition = "未定位" : $isPosition = "定位";
            //判断南北纬，0北纬，1南纬
            $isNorSou = substr($positionArray, -3, 1) == 0 ? $isNorSou = "北纬" : $isNorSou = "南纬";
            //判断东西经，0东经，1西经
            $isEasWes = substr($positionArray, -4, 1) == 0 ? $isEasWes = "东经" : $isEasWes = "西经";
            //4 0：运营状态；1：停运状态
            $is_dongxi = substr($positionArray, -5, 1) == 1 ? '停运状态' : '停运状态';
            //5 0：经纬度未经保密插件加密；1：经纬度已经保密插件加密
            $is_jiami = substr($positionArray, -6, 1) == 1 ? '经纬度已经保密插件加密' : '经纬度未经保密插件加密';
            //判断定位方式
            if (substr($positionArray, -19, 1) == 1 && substr($positionArray, -20, 1) == 0) {
                //北斗定位
                $positionMethod = "北斗定位";
            } elseif (substr($positionArray, -19, 1) == 0 && substr($positionArray, -20, 1) == 1) {
                //GPS定位
                $positionMethod = "GPS定位";
            } elseif (substr($positionArray, -19, 1) == 1 && substr($positionArray, -20, 1) == 1) {
                //北斗GPS双定位
                $positionMethod = "北斗GPS双定位";
            } else {
                //北斗GPS都未定位
                $positionMethod = "北斗GPS都未定位";
            }
            $positionAttr['position'] = $isPosition;
            $positionAttr['ns'] = $isNorSou;
            $positionAttr['ew'] = $isEasWes;
            $positionAttr['gps'] = $positionMethod;
            $positionAttr['is_dongxi'] = $is_dongxi;
            $positionAttr['jiami'] = $is_jiami;
            $positionAttr['is_acc'] = $is_acc;
            $zhzt = substr($positionArray, -10, 2);
            switch ($zhzt){
                case "00":
                    $positionAttr['is_jiami'] = '空车';
                    break;
                case "01":
                    $positionAttr['is_jiami'] = '半载';
                    break;
                case "10":
                    $positionAttr['is_jiami'] = '保留';
                    break;
                case "11":
                    $positionAttr['is_jiami'] = '满载（可用于客车的空、重车及货车的空载、满载状态表示，人工输入或传感器获取）';
                    break;
                default:
                    $positionAttr['is_jiami'] = '其他';
                    break;
            }
            //10 0：车辆油路正常；1：车辆油路断开
            $positionAttr['is_you'] = substr($positionArray, -11, 1) == 1 ? '车辆油路断开' : '车辆油路正常';
            //11 0：车辆电路正常；1：车辆电路断开
            $positionAttr['is_dian'] = substr($positionArray, -12, 1) == 1 ? '车辆电路断开' : '车辆电路正常';
            //12 0：车门解锁；1：车门加锁
            $positionAttr['is_chemen'] = substr($positionArray, -13, 1) == 1 ? '车门加锁' : '车门解锁';
            //13 0：门 1 关；1：门 1 开（前门）
            $positionAttr['is_men1'] = substr($positionArray, -14, 1) == 1 ? '门 1 开（前门）' : '门 1 关';
            //14 0：门 2 关；1：门 2 开（中门）
            $positionAttr['is_men2'] = substr($positionArray, -15, 1) == 1 ? '门 2 开（中门）' : '门 2 关';
            //15 0：门 3 关；1：门 3 开（后门）
            $positionAttr['is_men3'] = substr($positionArray, -16, 1) == 1 ? '门 3 开（后门）' : '门 3 关';
            //16 0：门 4 关；1：门 4 开（驾驶席门）
            $positionAttr['is_men4'] = substr($positionArray, -17, 1) == 1 ? '门 4 开（驾驶席门）' : '门 4 关';
            //17 0：门 5 关；1：门 5 开（自定义）
            $positionAttr['is_men5'] = substr($positionArray, -18, 1) == 1 ? '门 5 开（自定义）' : '门 5 关';
            //18 0：未使用 GPS 卫星进行定位；1：使用 GPS 卫星进行定位
            $positionAttr['is_gps'] = substr($positionArray, -19, 1) == 1 ? '使用 GPS 卫星进行定位' : '未使用 GPS 卫星进行定位';
            //19 0：未使用北斗卫星进行定位；1：使用北斗卫星进行定位
            $positionAttr['is_beidou'] = substr($positionArray, -20, 1) == 1 ? '使用北斗卫星进行定位' : '未使用北斗卫星进行定位';
            //20 0：未使用 GLONASS 卫星进行定位；1：使用 GLONASS 卫星进行定位
            $positionAttr['is_glonass'] = substr($positionArray, -21, 1) == 1 ?  '使用 GLONASS 卫星进行定位' : '未使用 GLONASS 卫星进行定位';
            //21 0：未使用 Galileo 卫星进行定位；1：使用 Galileo 卫星进行定位
            $positionAttr['is_galileo'] = substr($positionArray, -22, 1) == 1 ? '使用 Galileo 卫星进行定位' : '未使用 Galileo 卫星进行定位';
            //22-31 保留
            return $positionAttr;
        } else {
            return $positionArray;
        }
    }

    /**
     * description   获取纬度
     * @param $data array  16进制数组
     * @param $index  string 数组索引
     * @param $type  string 返回字符类型 i整形 f浮点形
     * @return float|int   纬度
     */
    public function getLatitude(array $data, string $index, string $type = 'f'): float|int
    {
        $latitudeBytes = array_slice($data, $index, 4);
        $latitude = $this->bytesToInt($latitudeBytes);
        $number="";
        if ($type == 'f') {
            $number = $latitude / pow(10, 6);
        }
        if ($type == 'i') {
            $number = $latitude;
        }
        return $number;
    }

    /**
     * description  获取经度
     * @param $data array  16进制数组
     * @param string $index 数组索引
     * @param string $type 返回字符类型 i整形 f浮点形
     * @return float|int|string 经度
     */
    public function getLongitude(array $data, string $index, string $type = 'f'): float|int|string
    {
        $longitudeBytes = array_slice($data, $index, 4);
        $longitude = $this->bytesToInt($longitudeBytes);
        $number="";
        if ($type == 'f') {
            $number = $longitude / pow(10, 6);
        }
        if ($type == 'i') {
            $number = $longitude;
        }
        return $number;
    }

    /**
     * [getHeight 获取高度]
     * @param array $data
     * @param string $index
     * @return int [type]  [高度]
     */
    public function getHeight(array $data, string $index): int
    {
        $heightBytes = array_slice($data, $index, 2);
        return $this->twoBytesToInteger($heightBytes);
    }

    /**
     * [getSpeed 获取速度]
     * @param array $data
     * @param string $index
     * @return int [type][速度]
     */
    public function getSpeed(array $data, string $index): int
    {
        $speedBytes = array_slice($data, $index, 2);
        return $this->twoBytesToInteger($speedBytes);
    }

    /**
     * [getDirection 获取方向]
     * @param array $data
     * @param string $index
     * @return int [type][方向]
     */
    public function getDirection(array $data, string $index): int
    {
        $directionBytes = array_slice($data, $index, 2);
        return $this->twoBytesToInteger($directionBytes);
    }

    /**
     * description  获取日期时间
     * @param $data array  16进制数组
     * @param string $index 数组索引35
     * @return string   日期时间字符串
     */
    public function getDatetime(array $data, string $index): string
    {
        $datetimeArray = array_slice($data, $index, 6);
        $res = [];
        foreach ($datetimeArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $datetime = $this->bcdToString($res);
        return "20" . substr($datetime, 0, 2) . "-" . substr($datetime, 2, 2) . "-" . substr($datetime, 4, 2) . " " . substr($datetime, 6, 2) . ":" . substr($datetime, 8, 2) . ":" . substr($datetime, 10, 2);
    }

    /**
     * description   获取平台流水号
     * @return array   返回流水号数组
     */
    public function getSequenceNumberArray(): array
    {
        //计算流水号
        $number = self::$sequenceNumber++;
        if ($number > 65025) {
            // 255 * 255 -1
            $number = 1;
        }
        //将十进制流水号换算成16进制流水号
        $get16Number = base_convert($number, 10, 16);
        $af = substr($get16Number, 0, 2);
        $bf = substr($get16Number, 2);
        $systemNumber = [];
        //判断
        if ($number > 0xff) {
            $systemNumber = array('0x' . $af, '0x' . $bf);
        } else {
            $systemNumber = array('0x00', '0x' . $get16Number);
        }
        foreach ($systemNumber as $k => $v) {
            $systemNumber[$k] = intval(base_convert($v, 16, 10));
        }
        return $systemNumber;
    }

    /**
     * description  获取消息流水号
     * @param $data 16进制数组
     * @return array  消息流水号数组
     */
    public function getMessageNumberArray($data): array
    {
        return array_slice($data, 11, 2);
    }

    /**
     * description  获取消息id
     * @param $data 16进制数组
     * @return array  消息id数组
     */
    public function getMessageIdArray($data): array
    {
        return array_slice($data, 1, 2);
    }

    /**
     * description  获取消息id
     * @param $data array 16进制数组
     * @param $length int 消息体长度 前位补0
     * @return bool|string   消息id字符串
     */
    public function getMessageIdNumber(array $data, int $length = 4): bool|string
    {
        $messageArray = array_slice($data, 1, 2);
        $res = [];
        foreach ($messageArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $messageNumber = $this->bcdToString($res);
        return str_pad($messageNumber, $length, "0", STR_PAD_LEFT);
    }

    /**
     * description   获取消息体
     * @param $data 16进制数组
     * @return array   消息体数组
     */
    public function getMessageBodyArray($data): array
    {
        //消息体 = 消息流水号 + 消息id
        //$messageId = $this->getMessageIdArray($data);
        //$messageBody = array_merge($messageNumber, $messageId);
        /*foreach ($messageBody as $k => $v) {
                $res[$k] = intval(base_convert($v, 16, 10));
            }
        */
        return $this->getMessageNumberArray($data);
    }

    /**
     * description  发送给客户端的回传数据
     * @param $data 16进制数组
     * @param string $auth
     * @return bool|string 返回客户端字符串
     */
    public function getVerifyNumberArray($data, string $auth = '31313131'): bool|string
    {
        //数组开始五位
        //$arrayStartFiveBytes = array('7E', '80', '01');
        //注册应答结果0
        $ret = array('00');
        //消息ID
        $messageId = $this->getMessageIdArray($data);
        $messageid = implode($messageId);
        //消息体
        $messageBody = $this->getMessageBodyArray($data);
        if ($messageid == '0100') {
            $arrayStartFiveBytes = array('7E', '81', '00');
            $jianquan = str_split($auth, 2);
            $messageBody = array_merge($messageBody, $ret, $jianquan);
        } else {
            $arrayStartFiveBytes = array('7E', '80', '01');
            $jianquan = [];
            $messageBody = array_merge($messageBody, $messageId, $ret, $jianquan);
        }
        //设备号
        $equipmentNumber = $this->getEquipmentNumberArray($data);
        //平台流水号
        //$systemNumber = $this->getSequenceNumberArray();
        $systemNumbers = $this->getMessageNumberArray($data);

        //消息体长度
        /*if ($messageid == '0100') {
            $msglength = count(array_merge($systemNumbers, $messageId, $ret, $jianquan));
        } else {
            $msglength = count(array_merge($systemNumbers, $messageId, $ret));
        }*/
        $msglength = count(array_merge($systemNumbers, $messageId, $ret, $jianquan));
        $msglength = decbin($msglength);

        //补齐16位，不加密，无版本号
        $attr = sprintf("%016d", $msglength); //消息体属性
        //前置补0
        $attr_str = str_pad(dechex(bindec($attr)), 4, '0', STR_PAD_LEFT);
        //$attr_str = dechex(bindec($attr));
        //分割字符
        $attrarray = str_split($attr_str, 2);
        //与消息体合并
        $array_attr = array_merge($arrayStartFiveBytes, $attrarray);
        //数组开始5位和设备号合并
        $arrayStartAndEquipmentNumber = array_merge($array_attr, $equipmentNumber);
        //接上一步继续与平台流水号合并
        $startEquipmentAndSystemNumber = array_merge($arrayStartAndEquipmentNumber, $systemNumbers);
        //接上一步继续与消息体合并
        $startEquipmentSystemAndMessageBody = array_merge($startEquipmentAndSystemNumber, $messageBody);
        //接上一步应答结果
        //$dataAndRet = array_merge($startEquipmentSystemAndMessageBody, $ret);
        $dataAndRet = $startEquipmentSystemAndMessageBody;
        //生成校验码
        //$dataAndRetXor = $this->getEveryXor($dataAndRet);
        $dataAndRetXor = $this->checkCode($dataAndRet);
        //数组末尾两位
        $arrayEndTwoBytes = array($dataAndRetXor, '7E');
        //整个数组
        $completeArray = array_merge($dataAndRet, $arrayEndTwoBytes);
        //发送给客户端的字符串
        return $this->arrayToBytes($completeArray);
    }
    public function getMessageTypeArray($data){
        return array_slice($data, 2, 2);
    }
    /**
     * description 获取消息体属性
     * @param $data array 16进制数组
     * @param int $length num 消息体长度 前位补0
     * @return array|string
     */
    public function getMessageType($data, $length = 4){
        $messageArray = $this->getMessageTypeArray($data);
        $res = [];
        foreach ($messageArray as $k => $v) {
            $res[$k] = base_convert($v, 16, 10);
        }
        $messageLength = '';
        foreach ($res as $k=>$v){
            $messageLength .= str_pad($v, 4, "0", STR_PAD_LEFT);
        }
        $messageLength = $messageLength . '0';
        $messageType = $this->bcdToString($res);
        $messageTypeStr = str_pad($messageType, $length, "0", STR_PAD_LEFT);
        $messageType2 = base_convert($messageTypeStr,10,2);
        $messageType2Str = str_pad($messageType2, 16, "0", STR_PAD_LEFT);

        //[0-9]消息体长度 0000,0011,1111,1111(3ff)
        //[10-12]加密类型 0001,1100,0000,0000(1c00)
        //[13] 是否有子包 0010,0000,0000,0000(2000)(是否有子包)
        //[14-15] 保留为 1100,0000,0000,0000(C000)
        $typeStr13 = substr($messageType2Str,2,1);
        $typeStr12 = substr($messageType2Str,3,1);
        $typeStr11 = substr($messageType2Str,4,1);
        $typeStr10 = substr($messageType2Str,5,1);
        $typeStr9_0 = substr($messageType2Str,6,10);
        return [
            'encryptionType' => $typeStr10 == 1 ? 'RSA' : (($typeStr10 == '0' && $typeStr11 == '0' && $typeStr12 == '0') ? false : ''), // 加密方式 RSA 不加密 保留
            'hasSunPackage' => $typeStr13 == 1 ? "1" : "0", // 1有分包 0无分包
            'messageLength' => $messageLength,
//            'messageLength' => base_convert(base_convert(str_pad($typeStr9_0, 12, "0", STR_PAD_LEFT),2,10),16,10),
            'messageTypeStr'=>$messageTypeStr,
        ];
    }

    public function getMessageBody($binaryData) {
        $data = implode('',$binaryData);
       return mb_substr($data, 26,-4, 'utf-8');
    }
    public function getMessageBodyData($data){
        $messageType = $this->getMessageType($data);
        if($messageType['hasSunPackage']){
            // 消息主体
            $MessageBody = array_slice($data, 14,$messageType['messageLength']);
        }else{
            $MessageBody = array_slice($data, 12,$messageType['messageLength']);
        }
        return $MessageBody;
    }

    //获取校验码
    static public function calCheckSum($hex_string)
    {
        $cs = hexdec(substr($hex_string, 0, 2));
        $str_len = strlen($hex_string);
        for ($i = 2; $i < $str_len; $i += 2) {
            $cs ^= hexdec(substr($hex_string, $i, 2));
        }
        return bin2hex(pack('C',$cs));
    }
    public function getTerminalRegistration($data_array)
    {
        $data['province_id'] = $this->getProvinceIdNumber($data_array);
        $data['city_id'] = $this->getCityIdNumber($data_array);
        $data['product_id'] = $this->getProducersIdNumber($data_array);
        $data['client_type'] = $this->getModelIdNumber($data_array);
        $data['client_id'] = $this->getEquipmentIdNumber($data_array);
        $data['car_number_color'] = $this->getCarColorNumber($data_array);
        $data['car_number'] = $this->getCarLogoNumber($data_array);
        return $data;
    }

    /*
        * description 把数组拼接成字符串
        * @param $data 数组 ['00','aa','02',...]
        * @return mixed 字符串
       */
    public function ArrayToString($data) {
        $str = '';
        foreach ($data as $ch) {
            $str .= $ch;
        }
        return $str;
    }


    /**
     * 获取省域 ID 数组
     * @param array $data
     * @return array
     */
    public function getProvinceIdArray(array $data){
        return array_slice($data,13,2);
    }

    /**
     * 获取省域 ID 字符串
     * @param array $data
     * @return array|string
     */
    public function getProvinceIdNumber(array $data){
        $cityIdArray = $this->getProvinceIdArray($data);
        return $this->ArrayToString($cityIdArray);
    }

    /**
     * 获取市县域 ID 数组
     * @return array
     */
    public function getCityIdArray(array $data){
        return array_slice($data,15,2);
    }

    /**
     * 获取市县域 ID 字符串
     * @param array $data
     * @return string
     */
    public function getCityIdNumber(array $data){
        $cityIdArray = $this->getCityIdArray($data);
        return $this->ArrayToString($cityIdArray);
    }

    /**
     * 制造商 ID 数组
     * @param array $data
     * @return array
     */
    public function getProducersIdArray(array $data){
        return array_slice($data,17,5);
    }

    /**
     * 制造商 ID 字符串
     * @return string
     */
    public function getProducersIdNumber(array $data){
        $producersIdArray = $this->getProducersIdArray($data);
        $producersIdNumber = $this->ArrayToString($producersIdArray);
        return pack('H*',$producersIdNumber);
    }

    /**
     * 终端型号
     * @param array $data
     * @return array
     */
    public function getModelIdArray(array $data){
        return array_slice($data,22,8);
    }

    /**
     * 终端型号 字符串
     * @param array $data
     * @return string
     */
    public function getModelIdNumber(array $data){
        $modelIdArray = $this->getModelIdArray($data);
        $equipmentIdNumber=  $this->ArrayToString($modelIdArray);
        return pack('H*',$equipmentIdNumber);
    }

    /**
     * 终端 ID 数组
     * @param array $data
     * @return array
     */
    public function getEquipmentIdArray(array $data){
        return array_slice($data,30,7);
    }

    /**
     * 终端 ID 字符串
     * @param array $data
     * @return string
     */
    public function getEquipmentIdNumber(array $data){
        $equipmentIdArray = $this->getEquipmentIdArray($data);
        $equipmentIdNumber = $this->ArrayToString($equipmentIdArray);
        return pack('H*',$equipmentIdNumber);
    }

    /**
     * 车牌颜色
     * @return array
     */
    public function getCarColorArray(array $data){
        return array_slice($data,37,1);
    }

    /**
     * 车牌颜色 字符串
     * @return mixed
     */
    public function getCarColorNumber(array $data){
        $carColorArray = $this->getCarColorArray($data);
        return $this->ArrayToString($carColorArray);
    }

    /**
     * 车牌标识
     * @return array
     */
    public function getCarLogoArray(array $data){
        return array_slice($data,37,9);
    }

    /**
     * 车牌标识 字符串
     * @param array $data
     * @return false|string
     */
    public function getCarLogoNumber(array $data){
        $carLogoArray = $this->getCarLogoArray($data);
        $carLogoNumber= $this->ArrayToString($carLogoArray);
        $carLogoNumber = pack('H*', $carLogoNumber);
        return iconv('GBK', 'utf-8', $carLogoNumber);
    }


    /**
     * 鉴权码的获取
     * @param array $data
     * @return string
     */
    public function getAuthenticationCode(array $data)
    {
        $carLogoArray = array_slice($data,13,6);
        $carLogoArray= $this->ArrayToString($carLogoArray);
        return pack('H*',$carLogoArray);
    }
}
